package framework.needscan.controller;

import framework.annotation.Controller;
import framework.annotation.RequestMapping;
import framework.bean.Action;
import framework.exception.RequestMappingHasMoreBlackRuntimeException;
import framework.scanner.BaseScanner;
import framework.util.StrRuleUtil;
import test.util.Test;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 马洁
 * @date 2020/3/9 - 18:00
 */
public class ControllerScanner extends BaseScanner {
    private static ControllerScanner scanner = new ControllerScanner();
    private static int defaultSize = 16;
    private Map<String, Action> requestMapping;

    private ControllerScanner() {
        requestMapping = new ConcurrentHashMap<>(defaultSize);
    }

    public static ControllerScanner getInstance() {
        return scanner;
    }

    public static void setDefaultSize(int defaultSize) {
        ControllerScanner.defaultSize = defaultSize;
    }

    @Override
    synchronized
    public void scan(Class<?> cls) {

        if (Objects.nonNull(cls.getAnnotation(Controller.class))) {
            if (Objects.nonNull(cls.getAnnotation(RequestMapping.class))) {
                assembleClassLevelReqMapping(cls);
            } else {
                assembleMethodLevelReqMapping(cls);
            }
        }
    }

    public void handleReq(String reqUrl) {
        Test.p("reqUrl = " + reqUrl);
        Test.p(requestMapping);

        requestMapping.get(reqUrl).exeMethod(getObjectPool());
    }

    public Map<String, Action> getRequestMapping() {
        return requestMapping;
    }

    private void assembleClassLevelReqMapping(Class<?> cls) {

        final String requestVal = cls.getAnnotation(RequestMapping.class).value();

        for (Method method : cls.getDeclaredMethods()) {
            final Action action = new Action(cls, method);
            action.judgeAndSetArgs();

            putReqUrlInReqUrlMap(assembleReqUrl(requestVal, methodReqVal(method)),
                    action);
        }
    }

    private void assembleMethodLevelReqMapping(Class<?> cls) {
        final String clsName = cls.getSimpleName();

        for (Method method : cls.getDeclaredMethods()) {
            if (Objects.nonNull(method.getAnnotation(RequestMapping.class))) {
                final Action action = new Action(cls, method);

                putReqUrlInReqUrlMap(assembleReqUrl(clsName, methodReqVal(method)),
                        action);
            }
        }
    }

    /**
     * 获取方法的映射值
     *
     * @param method 方法
     * @return 方法对应的映射值
     */
    private String methodReqVal(Method method) {
        RequestMapping reqMapping;

        return Objects.nonNull(reqMapping = method.getAnnotation(RequestMapping.class)) ?
                reqMapping.value() :
                method.getName();
    }

    private void putReqUrlInReqUrlMap(String reqUrl, Action action) {
        if (requestMapping.containsKey(reqUrl)) {
            throw new RequestMappingHasMoreBlackRuntimeException();
        }

        requestMapping.put(reqUrl, action);
    }

    private String assembleReqUrl(String controllerReq, String methodReq) {
        return controllerReq + StrRuleUtil.getStandardReqUrl(methodReq);
    }
}
